package com.lx.boot.db;

import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.lx.annotation.Note;
import com.lx.boot.db.injector.MpSqlInjector;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import java.io.IOException;
import java.util.Properties;

@Configuration
@ConditionalOnBean(DynamicDataSource.class)
public class DynamicSqlSessionFactory implements BeanPostProcessor {

    public DynamicSqlSessionFactory(DynamicDataSource dynamicDataSource) throws IOException {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        // 配置数据源，此处配置为关键配置，如果没有将 dynamicDataSource作为数据源则不能实现切换
        sessionFactory.setDataSource(dynamicDataSource);
    }


    private final String PROPAGATION_NAME_CHANGE = "PROPAGATION_REQUIRED,-Exception";

    @Autowired
    private DataSourceTransactionManager transactionManager;
    // 创建事务通知
    @Bean(name = "txAdvice")
    @ConditionalOnMissingBean()
    public TransactionInterceptor transactionInterceptor() throws Exception {
        Properties properties = new Properties();
        properties.setProperty("add*", PROPAGATION_NAME_CHANGE);
        properties.setProperty("save*", PROPAGATION_NAME_CHANGE);
        properties.setProperty("insert*", PROPAGATION_NAME_CHANGE);
        properties.setProperty("update*", PROPAGATION_NAME_CHANGE);
        properties.setProperty("delete*", PROPAGATION_NAME_CHANGE);
        TransactionInterceptor tsi = new TransactionInterceptor(transactionManager, properties);
        return tsi;
    }
    @Bean
    @ConditionalOnBean(TransactionInterceptor.class)
    @ConditionalOnMissingBean()
    public BeanNameAutoProxyCreator txProxy() {
        BeanNameAutoProxyCreator creator = new BeanNameAutoProxyCreator();
        creator.setInterceptorNames("txAdvice");
        creator.setBeanNames("*Service", "*ServiceImpl");//指定后缀类加事务
        creator.setProxyTargetClass(true);
        return creator;
    }

    /**
     * PaginationInnerInterceptor 分页插件，自动识别数据库类型
     * https://baomidou.com/pages/97710a/
     * OptimisticLockerInnerInterceptor 乐观锁插件
     * https://baomidou.com/pages/0d93c0/
     * MetaObjectHandler 元对象字段填充控制器
     * https://baomidou.com/pages/4c6bcf/
     * ISqlInjector sql注入器
     * https://baomidou.com/pages/42ea4a/
     * BlockAttackInnerInterceptor 如果是对全表的删除或更新操作，就会终止该操作
     * https://baomidou.com/pages/f9a237/
     * IllegalSQLInnerInterceptor sql性能规范插件(垃圾SQL拦截)
     * IdentifierGenerator 自定义主键策略
     * https://baomidou.com/pages/568eb2/
     * FirmLineInnerInterceptor 多租户插件
     * https://baomidou.com/pages/aef2f2/
     * DynamicTableNameInnerInterceptor 动态表名插件
     * https://baomidou.com/pages/2a45ff/
     */

    /** 分页配置 */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(paginationInnerInterceptor() );
        interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());

        //动态数据权限
//        interceptor.addInnerInterceptor(dataPermissionInterceptor());
        return interceptor;
    }
    @Note("分页插件")
    public PaginationInnerInterceptor paginationInnerInterceptor() {
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        // 设置最大单页限制数量，默认 500 条，-1 不受限制
        paginationInnerInterceptor.setMaxLimit(-1L);
        // 分页超出后返回第一页
        paginationInnerInterceptor.setOverflow(true);
        return paginationInnerInterceptor;
    }

    /**
     *
     * 需要进行全表操作时,示例:
     * @InterceptorIgnore(blockAttack = "true")
     * @Delete("delete from env_check_house")
     * int deleteEnvCheckHouseAll();
     */
    @Note("防止修改与删除时对全表进行操作 在Mapper方法上使用 @InterceptorIgnore(blockAttack = \"true\") 可以进行全表操作")
    public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {
        return new BlockAttackInnerInterceptor();
    }

    @Note("乐观锁插件")
    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
        return new OptimisticLockerInnerInterceptor();
    }

    @Note("动态数据权限,自动拼接where条件")
    public PlusDataPermissionInterceptor dataPermissionInterceptor() {
        return new PlusDataPermissionInterceptor();
    }

    @Bean
    @Note("自动填充新增更新用户和时间")
    public MetaObjectHandler metaObjectHandler() {
        return new InjectionMetaObjectHandler();
    }

//    @Bean
//    @Note("防止集群雪花ID重复")
//    public IdentifierGenerator idGenerator() {
//        return new DefaultIdentifierGenerator(NetUtil.getLocalhost());
//    }

    @Bean
    @Note("注册物理删除的sql")
    public ISqlInjector sqlInjector() {
        return new MpSqlInjector();
    }

    @Bean
    @Note("Map返回值时使用驼峰")
    @ConditionalOnMissingBean()
    public ConfigurationCustomizer mybatisConfigurationCustomizer(){
        return new ConfigurationCustomizer() {
            @Override
            public void customize(MybatisConfiguration configuration) {
                configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory());
            }
        };
    }
}
